package org.apache.ibatis.common.logging.jdbc;

import org.apache.ibatis.common.logging.Log;
import org.apache.ibatis.common.reflection.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.interfaces.ECKey;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * @Name PreparedStatementLogger
 * @Description: [JDBC PreparedStatement 对象代理（日志增强）] 实现类
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-05-15 17:39:56
 **/
public class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
    private final PreparedStatement statement;

    private PreparedStatementLogger(PreparedStatement statement, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.statement = statement;
    }

    public static PreparedStatement newInstance(PreparedStatement ps, Log statementLog, int queryStack) {
        InvocationHandler handler = new PreparedStatementLogger(ps, statementLog, queryStack);
        ClassLoader loader = PreparedStatement.class.getClassLoader();
        return (PreparedStatement) Proxy.newProxyInstance(loader, new Class[]{PreparedStatement.class}, handler);
    }

    public PreparedStatement getPreparedStatement() {
        return statement;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果方法继承自 Object 类，交由当前对象执行
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            // 拦截 PreparedStatement 中的 execute、executeUpdate、executeQuery、executeQuery 方法
            if (EXECUTE_METHODS.contains(method.getName())) {
                // 打印日志
                if (isDebugEnabled()) debug(" Parameters: " + getParameterValueString(), true);
                // 清除缓存
                clearColumnInfo();
                // 如果目标方法为 executeQuery 方法，返回 ResultSet 代理对象，该代理中加入了对 ResultSet 的日志打印操作
                if ("executeQuery".equals(method.getName())) {
                    ResultSet rs = (ResultSet) method.invoke(statement, args);
                    return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
                } else return method.invoke(statement, args);
            }
            // 拦截 PreparedStatement 中的所有 Setter 方法，放入缓存
            else if (SET_METHODS.contains(method.getName())) {
                if ("setNull".equals(method.getName())) setColumn(args[0], null);
                else setColumn(args[0], args[1]);
                return method.invoke(statement, args);
            }
            // 拦截 PreparedStatement 中的 getResultSet 方法
            else if ("getResultSet".equals(method.getName())) {
                // 返回 ResultSet 代理对象，该代理中加入了对 ResultSet 的日志打印操作
                ResultSet rs = (ResultSet) method.invoke(statement, args);
                return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
            }
            // 拦截 PreparedStatement 中的 getUpdateCount 方法
            else if ("getUpdateCount".equals(method.getName())) {
                int updateCount = (int) method.invoke(statement, args);
                // 打印日志
                if (updateCount != -1) debug(" Update: " + updateCount, false);
                return updateCount;
            }
            // 其他方法，直接放行
            else {
                return method.invoke(statement, args);
            }
        } catch (Throwable e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }
}

